package leafedbox.utils
{
	import mx.core.Container;
	import mx.core.UIComponent;

	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;

	/**
	 *  A utility class for working with components and hierarchies.
	 */
	public class ComponentUtil
	{
		//----------------------------------------------------------------------
		// Static methods
		//----------------------------------------------------------------------

		/**
		 * Traverses the parent hierarchy to find the first object of a specific subtype.
		 *
		 * @param component  The component to search.
		 * @param class      The subtype to match.
		 *
		 * @return  A <code>UIComponent</code> object with a subtype of <code>classRef</code>.
		 */
		public static function getParentByType(component:DisplayObject, classRef:Class):*
		{
			var comp : DisplayObjectContainer = component.parent;

			// Step up the parent chain until we find a DisplayObjectContainer that matches our class.
			while(comp != null) {
				if(comp is classRef) {
					break;
				}

				comp = comp.parent;
			}

			return comp;
		}


		/**
		 * Traverses the parent hierarchy to find all objects of a specific subtype.
		 *
		 * @param component  The component to search.
		 * @param class      The subtype to match.
		 *
		 * @return  An <code>Array</code> of objects with the subtype of <code>classRef</code>.
		 */
		public static function getParentsByType(component:DisplayObject, classRef:Class):Array
		{
			var arr  : Array = new Array();
			var comp : DisplayObjectContainer = component.parent;

			// Step up the parent chain until we find a DisplayObjectContainer that matches our class.
			while(comp != null && comp.parent != null) {
				if(comp is classRef) {
					arr.push(comp);
				}

				comp = comp.parent;
			}

			return arr;
		}


		/**
		 * Traverses the child hierarchy to find an array of objects that are a 
		 * specific subtype of the <code>UIComponent</code> class.
		 *
		 * @param container  The component to search.
		 * @param class      The subtype to match.
		 * @param include    A flag stating whether use the <code>class</code> parameter
		 *                   as an inclusion or exclusion.  When used as an exclusion,
		 *                   all classes except the one passed in will be returned.
		 * @param list       Optional.  This is the array the children are appended to.
		 *                   It is created if it did not exist.
		 *
		 * @return  A list of <code>DisplayObject</code> objects that match the subtype of 
		 *          the <code>class</code> argument.  This object is the same as the 
		 *          <code>list</code> argument if it was passed in.
		 */
		public static function getChildrenByType(component:DisplayObject, classRef:Class, exclude:Boolean = false, list:Array = null):Array
		{
			//  Create a new array if nothing was passed in.
			if(list == null) {
				list = new Array();
			}

			// Push to array if it matches the class.
			if( (!exclude && component is classRef) || (exclude && !(component is classRef))) {
				list.push(component);
			}

			// Recursively traverse the hierarchy
			if(component is Container) {
				var count : int = (component as Container).numChildren;

				for(var i:int=0; i<count; i++) {
					getChildrenByType((component as Container).getChildAt(i), classRef, exclude, list);
				}
			}

			// Return the list
			return list;
		}    


		/**
		 * Removes a <code>DisplayObject</code> from its parent.  This method automatically
		 * checks for the existence of the object and the object's parent.  This method is 
		 * provided as an ease-of-use method.
		 *
		 * @param component  The component to remove.
		 */
		public static function remove(obj:DisplayObject):void
		{
			if(obj != null && obj.parent != null) {
				obj.parent.removeChild(obj);
			}
		}
	}

}
